using System.Collections.Generic;


namespace Casters.source
{
	/// <summary>
	/// Enumeration that stores all possible events. This would be best held outside, but for now this works.
	/// </summary>
	public enum EventID { 
        // Used for testing
        // No params
        Test = 0,

        // Changes Active state
        // No params
        ChangeState,

        // Event to close game
        // No params
        ExitGame,

        // Direction up pressed
        // No params
        Up,

        // Direction right pressed
        // No params
        Right,

        // Direction down pressed
        // No params
        Down,

        // Direction left pressed
        // No params
        Left,

        // Direction up held
        // No params
        UpHeld,

        // Direction right held
        // No params
        RightHeld,

        // Direction down held
        // No params
        DownHeld,

        // Direction left held
        // No params
        LeftHeld,

        // Confirm selection, enter, yes
        // No params
        Confirm,

        // Back, cancel selection, no
        // No params
        Back,

        // Any mouse movement
        // [0] Vector2 mouse position
        MouseMove,

        // Mouse Left Click
        // [0] Vector2 mouse position
        MouseLeftClick,

        // Mouse Right Click
        // [0] Vector2 mouse position
        MouseRightClick,

        // Player clicked the Move button while in battle
        // No params
        BattleUI_MoveClicked,

        // Player clicked the Combat button while in battle
        // No params
        BattleUI_CombatClicked,

        // Player clicked the End Turn button while in battle
        // No params
        BattleUI_EndTurnClicked,

        // Player clicked a spell button while in battle
        // [0] Spell slot number clicked
        BattleUI_SpellClicked,

        // Moves the caster up
        // [0] int Turn Number
        Move_PlayerUp,

        // Moves the caster down
        // [0] int Turn Number
        Move_PlayerDown,

        // Moves the caster left
        // [0] int Turn Number
        Move_PlayerLeft,

        // Moves the caster right
        // [0] int Turn Number
        Move_PlayerRight,

        // Moves the Tile Cursor Up
        // No params
        Move_TileCursorUp,

        // Moves the Tile Cursor Down
        // No params
        Move_TileCursorDown,

        // Moves the Tile Cursor Left
        // No params
        Move_TileCursorLeft,

        // Moves the Tile Cursor Right
        // No params
        Move_TileCursorRight,

        // Removes the Tile Cursor from the Object Manager
        // No params
        TileCursor_Kill,

        // Player has run out of HP
        // [0] int The player that died
        Player_Dead,

        // A spell render object is to be created
        // [0] string The spell to render
        CreateSpell,

        // Adds an action to the combat queue
        // [0] The CRenderObject that is the owner(Player)
        // [1] The tick this action should activate on
        // [2] The type of the action(Active or Rest)
        // [3] The eventID to fire off when activated
        // [4] The target of the spell
        // [5] The index of the spell in the player's spell list
        AddAction,

        // Combat has started
        // No params
        StartOfCombat,

        // Combat has finished
        // No params
        EndOfCombat,

        // Replaying of all actions has started
        // No params
        StartOfReplay,

        // Replaying of all actions has finished
        // No params
        EndOfReplay,

        // Add Scrolling Text to the screen
        // [0] String of text to use
        // [1] Vector2 position
        AddScrollingText,

        // Add movement to the player queue
        // [0] Player's Direction
        Setup_AddMove,

        // Casts a spell during execute state
        // [0] The target
        // [1] The spell to use
        // [2] The owner of the spell
        Execute_CastSpell,

        // Spell has reached it's target
        // [0] The target
        // [1] The spell
        Execute_SpellHitsTarget,

        // Resets the player's move direction
        // No params
        Execute_ResetMove,

        // Resets the computer's move direction
        // No params
        Execute_ResetMove_Computer,

        // End of the AI tree has been reached
        // No params
        AI_EndOfTree,

        Max };


	/// <summary>
	/// The callback function that all event handler functions
	/// must comply with.
	/// </summary>
	/// <param name="eventArguments">A parameterized list of objects for an event</param>
	delegate void EventCallback(params object[] eventArguments);

	/// <summary>
	/// Singleton class that manages events
	/// </summary>
	static class CEventSystem
	{
		/// <summary>
		/// Helper class that contains all information pertaining
		/// to an individual event.
		/// </summary>
		private class EventInfo
		{
			private EventID m_EventID;
			private EventCallback m_FunctionToCall = null;
			private object[] m_EventArgs;

			/// <summary>
			/// Constructor for the EventInfo class
			/// </summary>
			/// <param name="_eventID">The enum ID of the event to send</param>
			/// <param name="_eventArgs">Array off objects from the event's parameterized list</param>
			/// <param name="_callbackFunction">The function to call if directly sending an event</param>
			public EventInfo(EventID _eventID, object[] _eventArgs, EventCallback _callbackFunction)
			{
				m_EventID = _eventID;
				m_EventArgs = _eventArgs;
				m_FunctionToCall = _callbackFunction;
			}

			/// <summary>
			/// Constructor for the EventInfo class
			/// </summary>
			/// <param name="_eventID">The enum ID of the event to send</param>
			/// <param name="_eventArgs">Array off objects from the event's parameterized list</param>
			public EventInfo(EventID _eventID, object[] _eventArgs)
			{
				m_EventID = _eventID;
				m_EventArgs = _eventArgs;
			}

			public EventID GetEventID() { return m_EventID; }
			public object[] GetEventArguments() { return m_EventArgs; }
			public EventCallback GetCallbackFunction() { return m_FunctionToCall; }
		}

		static private List<EventCallback>[] m_EventObjectPairing;
		static private List<EventInfo> m_EventList;
		static private List<EventInfo> m_DispatchingEventList;

		/// <summary>
		/// Initializes the event system for use
		/// </summary>
		static public void Initialize()
		{
			m_EventObjectPairing = new List<EventCallback>[(int)EventID.Max];
			m_EventList = new List<EventInfo>();
			m_DispatchingEventList = new List<EventInfo>();

			for (int i = 0; i < (int)EventID.Max; ++i)
			{
				m_EventObjectPairing[i] = new List<EventCallback>();
			}
		}

		/// <summary>
		/// Registers an event with a handling function
		/// </summary>
		/// <param name="_eventID">The enum ID representing the event</param>
		/// <param name="_handlingFunction">The function that is called when the event occurs</param>
		static public void RegisterEventHandler (EventID _eventID, EventCallback _handlingFunction)
		{
			m_EventObjectPairing[(int)_eventID].Add(_handlingFunction);
		}

		/// <summary>
		/// Unregisters a function from a given event
		/// </summary>
		/// <param name="_eventID">The event the function no longer will listen for</param>
		/// <param name="_handingFunction">The function to unregister</param>
		static public void UnregisterEventHandler(EventID _eventID, EventCallback _handingFunction)
		{
			m_EventObjectPairing[(int)_eventID].Remove(_handingFunction);
		}

		/// <summary>
		/// Clears all callback functions registered to a given event
		/// </summary>
		/// <param name="_eventID">The enum ID for the given event</param>
		static public void UnregisterAllEventHandlers(EventID _eventID)
		{
			m_EventObjectPairing[(int)_eventID].Clear();
		}

		/// <summary>
		/// Sends an event to a particular function
		/// </summary>
		/// <param name="_eventID">The enum ID for the event to send</param>
		/// <param name="_handlingFunction">The function that will respond to the given event</param>
		/// <param name="_eventArgs">A parameterized list that will contain all data sent with the event</param>
		static public void SendEvent(EventID _eventID, EventCallback _handlingFunction, params object[] _eventArgs)
		{
			EventInfo tempEvent = new EventInfo(_eventID, _eventArgs, _handlingFunction);
			m_EventList.Add(tempEvent);
		}

		/// <summary>
		/// Broadcasts an event to all listeners of that event
		/// </summary>
		/// <param name="_eventID">The enum ID for the event to send</param>
		/// <param name="_eventArgs">A parameterized list that will contain all data sent with the event</param>
		static public void BroadcastEvent(EventID _eventID, params object[] _eventArgs)
		{
			EventInfo tempEvent = new EventInfo(_eventID, _eventArgs);
			m_EventList.Add(tempEvent);
		}

		/// <summary>
		/// Sends all events that have accumulated since the last dispatch to
		/// their appropriate listeners. Events are sent from a copied list, letting
		/// events send events of their own that will be handled on DispatchEvents' next call.
		/// </summary>
		static public void DispatchEvents()
		{
			m_DispatchingEventList.AddRange(m_EventList);
			m_EventList.Clear();

			foreach (EventInfo dispatchingEvent in m_DispatchingEventList)
			{
				if (dispatchingEvent.GetCallbackFunction() != null)
				{
					dispatchingEvent.GetCallbackFunction()(dispatchingEvent.GetEventArguments());
				}
				else
				{
					int currentEventID = (int)dispatchingEvent.GetEventID();

					foreach (EventCallback callbackFunction in m_EventObjectPairing[currentEventID])
					{
						callbackFunction(dispatchingEvent.GetEventArguments());
					}
				}
			}



			m_DispatchingEventList.Clear();
		}
	}
}